﻿using System;
using System.Collections.Generic;
using System.Globalization;

namespace Gmantis.Controls
{
    internal class TokenizedMask
    {
        // Fields
        private readonly ContextDependentLiteralToActualSymbolDelegateMap _contextLiteralToValueDelegateMap;
        private CultureInfo _culture;
        private IList<string> _maskSymbols;
        private string _promptPresenter;
        private readonly PromptSymbolToMatchDelegateMap _symbolToMatchDelegateMap;
        private readonly MaskTokenCollection _tokens;
        private const string DefaultPromptPresenter = "_";

        // Methods
        public TokenizedMask()
        {
            PromptSymbolToMatchDelegateMap map = new PromptSymbolToMatchDelegateMap();
            map.Add("0", new PromptSymbolInfo(s => char.IsDigit(s, 0), true));
            map.Add("9", new PromptSymbolInfo(s => char.IsDigit(s, 0), false));
            map.Add("#", new PromptSymbolInfo(delegate(string s)
            {
                if (!char.IsDigit(s, 0) && !(s == "+"))
                {
                    return s == "-";
                }
                return true;
            }, false));
            map.Add("L", new PromptSymbolInfo(s => char.IsLetter(s, 0), true));
            map.Add("?", new PromptSymbolInfo(s => char.IsLetter(s, 0), false));
            map.Add("A", new PromptSymbolInfo(s => char.IsLetterOrDigit(s, 0), true));
            map.Add("a", new PromptSymbolInfo(s => char.IsLetterOrDigit(s, 0), false));
            map.Add("&", new PromptSymbolInfo(s => true, true));
            map.Add("C", new PromptSymbolInfo(s => true, false));
            this._symbolToMatchDelegateMap = map;
            ContextDependentLiteralToActualSymbolDelegateMap map2 = new ContextDependentLiteralToActualSymbolDelegateMap();
            map2.Add(".", (symbol, ownerMask) => ownerMask.Culture.NumberFormat.CurrencyDecimalSeparator);
            map2.Add(",", (symbol, ownerMask) => ownerMask.Culture.NumberFormat.CurrencyGroupSeparator);
            map2.Add(":", (symbol, ownerMask) => DateTime.Now.ToString("hh:m", ownerMask.Culture.DateTimeFormat)[2].ToString());
            map2.Add("/", (symbol, ownerMask) => DateTime.Now.ToString("dd/MM", ownerMask.Culture.DateTimeFormat)[2].ToString());
            map2.Add("$", (symbol, ownerMask) => ownerMask.Culture.NumberFormat.CurrencySymbol);
            this._contextLiteralToValueDelegateMap = map2;
            this._tokens = new MaskTokenCollection();
            this._promptPresenter = "_";
            this._maskSymbols = new List<string>();
        }

        private void BeginEdit()
        {
            foreach (MaskToken token in this.Tokens)
            {
                token.BeginEdit();
            }
        }

        private int CalcTrailingBlankTokens(int mostLeftIdx)
        {
            int num = 0;
            int num2 = Math.Max(mostLeftIdx, 0);
            for (int i = this.Tokens.Count - 1; i >= num2; i--)
            {
                PromptToken token = this.Tokens[i] as PromptToken;
                if ((token != null) && token.IsEmpty)
                {
                    num++;
                }
            }
            return num;
        }

        private SymbolMatchResult CanApplySymbolToToken(string symbol, MaskToken token)
        {
            if (token.MatchSymbol(symbol))
            {
                if (!token.IsLiteral)
                {
                    return SymbolMatchResult.MatchedPrompt;
                }
                return SymbolMatchResult.MatchedLiteral;
            }
            if (!token.IsLiteral)
            {
                return SymbolMatchResult.InvalidPrompt;
            }
            return SymbolMatchResult.InvalidLiteral;
        }

        private SymbolMatchResult CanApplySymbolToTokenWithSkip(string symbol, int tokenIdx, out int actualTokenIdx)
        {
            MaskToken token = this.Tokens[tokenIdx];
            SymbolMatchResult result = this.CanApplySymbolToToken(symbol, token);
            actualTokenIdx = tokenIdx;
            if (result == SymbolMatchResult.InvalidLiteral)
            {
                int num = this.FindNextPrompt(tokenIdx, true);
                if (num >= 0)
                {
                    result = this.CanApplySymbolToToken(symbol, this.Tokens[num]);
                    actualTokenIdx = num;
                }
            }
            return result;
        }

        private void CancelEdit()
        {
            foreach (MaskToken token in this.Tokens)
            {
                token.CancelEdit();
            }
        }

        private void EndEdit()
        {
            foreach (MaskToken token in this.Tokens)
            {
                token.EndEdit();
            }
        }

        private int FindNextPrompt(int offset, bool toTheRight)
        {
            int num2;
            int num3;
            int num4;
            int count = this.Tokens.Count;
            if (offset < -1)
            {
                offset = -1;
            }
            else if (offset > count)
            {
                offset = count;
            }
            if (toTheRight)
            {
                num2 = 1;
                num4 = offset + 1;
                num3 = count;
            }
            else
            {
                num2 = -1;
                num4 = offset - 1;
                num3 = -1;
            }
            for (int i = num4; i != num3; i += num2)
            {
                if (!this.Tokens[i].IsLiteral)
                {
                    return i;
                }
            }
            return -1;
        }

        private IList<string> GetApplicableSymbolList(IList<string> symbols, int symbolIdx, int tokenIdx, int promptCount)
        {
            List<string> list = new List<string>(symbols.Count);
            int num = symbolIdx;
            int num2 = tokenIdx;
            while (((num < symbols.Count) && (num2 < this.Tokens.Count)) && (promptCount > 0))
            {
                int num3;
                string symbol = symbols[num];
                SymbolMatchResult result = this.CanApplySymbolToTokenWithSkip(symbol, num2, out num3);
                bool flag = true;
                switch (result)
                {
                    case SymbolMatchResult.MatchedPrompt:
                        promptCount--;
                        break;

                    case SymbolMatchResult.MatchedLiteral:
                        break;

                    default:
                        flag = false;
                        break;
                }
                if (flag)
                {
                    list.Add(symbol);
                    num2 = num3 + 1;
                }
                num++;
            }
            return list;
        }

        public bool GetIsValid()
        {
            for (int i = 0; i < this.Tokens.Count; i++)
            {
                PromptToken token = this.Tokens[i] as PromptToken;
                if (((token != null) && token.IsEmpty) && this.IsRequiredPromptSymbol(token.OriginalSymbol))
                {
                    return false;
                }
            }
            return true;
        }

        internal Func<string, bool> GetMatchPromptDelegate(string promptSymbol)
        {
            return this._symbolToMatchDelegateMap[promptSymbol].MatchDelegate;
        }

        private int GetTextLength(int tokenCount)
        {
            int num = 0;
            for (int i = 0; i < tokenCount; i++)
            {
                num += this.Tokens[i].OutputSymbol.Length;
            }
            return num;
        }

        public IList<string> GetTextSymbols()
        {
            return this.GetTextSymbols(MaskFormat.IncludePromptAndLiterals, false);
        }

        public IList<string> GetTextSymbols(MaskFormat maskFormat)
        {
            return this.GetTextSymbols(maskFormat, false);
        }

        public IList<string> GetTextSymbols(MaskFormat maskFormat, bool promptsAsSpaces)
        {
            bool flag = (maskFormat == MaskFormat.IncludePrompt) || (maskFormat == MaskFormat.IncludePromptAndLiterals);
            bool flag2 = (maskFormat == MaskFormat.IncludeLiterals) || (maskFormat == MaskFormat.IncludePromptAndLiterals);
            List<string> list = new List<string>(this.Tokens.Count);
            for (int i = 0; i < this.Tokens.Count; i++)
            {
                bool flag3;
                bool isEmpty = false;
                MaskToken token = this.Tokens[i];
                if (token.IsLiteral)
                {
                    flag3 = flag2;
                }
                else
                {
                    isEmpty = ((PromptToken)token).IsEmpty;
                    flag3 = !isEmpty || flag;
                }
                if (flag3)
                {
                    list.Add((isEmpty && promptsAsSpaces) ? " " : token.OutputSymbol);
                }
            }
            return list;
        }

        public bool IsContextDependentLiteral(string symbol)
        {
            return this._contextLiteralToValueDelegateMap.ContainsKey(symbol);
        }

        public bool IsControlSymbol(string symbol)
        {
            string str;
            if (((str = symbol) == null) || ((!(str == "<") && !(str == ">")) && !(str == "|")))
            {
                return false;
            }
            return true;
        }

        public bool IsCustomPromptMarker(string symbol)
        {
            return (symbol == ";");
        }

        public bool IsPromptSymbol(string symbol)
        {
            return this._symbolToMatchDelegateMap.ContainsKey(symbol);
        }

        public bool IsRequiredPromptSymbol(string symbol)
        {
            return this._symbolToMatchDelegateMap[symbol].IsRequired;
        }

        public bool IsResetSymbol(string symbol)
        {
            if (!(symbol == this.PromptPresenter))
            {
                return (symbol == " ");
            }
            return true;
        }

        public bool ProcessTextChange(int offset, IList<string> replacedSymbols, IList<string> addedSymbols, int deletedSymbolsCount, bool emulateReplace, out int cursorPosition)
        {
            cursorPosition = -1;
            int num = (addedSymbols != null) ? addedSymbols.Count : 0;
            int num2 = (replacedSymbols != null) ? replacedSymbols.Count : 0;
            int count = this.Tokens.Count;
            bool flag = false;
            IList<string> symbols = new List<string>(num2 + num);
            if (num2 > 0)
            {
                ((List<string>)symbols).AddRange(replacedSymbols);
            }
            if (num > 0)
            {
                ((List<string>)symbols).AddRange(addedSymbols);
            }
            int num4 = symbols.Count;
            bool flag2 = emulateReplace;
            this.BeginEdit();
            try
            {
                int tokenIdx = offset;
                int symbolIdx = 0;
                int num7 = num2 + deletedSymbolsCount;
                bool flag3 = false;
                if (num4 > 0)
                {
                    bool? nullable = ((num2 > 0) || flag2) ? null : ((bool?)true);
                    while (((symbolIdx < symbols.Count) && !flag) && (tokenIdx < count))
                    {
                        int num8;
                        PromptToken token2;
                        string symbol = symbols[symbolIdx];
                        MaskToken token = this.Tokens[tokenIdx];
                        switch (this.CanApplySymbolToTokenWithSkip(symbol, tokenIdx, out num8))
                        {
                            case SymbolMatchResult.MatchedPrompt:
                                if (nullable.HasValue)
                                {
                                    goto Label_013F;
                                }
                                if (num8 == tokenIdx)
                                {
                                    break;
                                }
                                tokenIdx = num8;
                                goto Label_01CC;

                            case SymbolMatchResult.MatchedLiteral:
                                symbolIdx++;
                                tokenIdx++;
                                goto Label_01CC;

                            case SymbolMatchResult.InvalidPrompt:
                                tokenIdx = num8;
                                symbolIdx++;
                                goto Label_01CC;

                            case SymbolMatchResult.InvalidLiteral:
                                tokenIdx++;
                                goto Label_01CC;

                            default:
                                goto Label_01CC;
                        }
                        ((PromptToken)token).Value = symbol;
                        flag3 = true;
                        tokenIdx++;
                        symbolIdx++;
                        goto Label_01CC;
                    Label_013F:
                        token2 = (PromptToken)this.Tokens[num8];
                        if (!token2.IsEmpty && nullable.Value)
                        {
                            nullable = false;
                            int promptCount = this.CalcTrailingBlankTokens(num8 + 1);
                            symbols = this.GetApplicableSymbolList(symbols, symbolIdx, num8, promptCount);
                            symbolIdx = 0;
                            flag = (promptCount == 0) || !this.ShiftSymbols(num8, symbols.Count);
                        }
                        token2.Value = symbol;
                        flag3 = true;
                        symbolIdx++;
                        tokenIdx = num8 + 1;
                    Label_01CC:
                        if ((!flag2 && ((tokenIdx - offset) >= num7)) && !nullable.HasValue)
                        {
                            nullable = true;
                        }
                    }
                }
                if (!flag)
                {
                    cursorPosition = this.GetTextLength(tokenIdx);
                    int num10 = num7 - (tokenIdx - offset);
                    flag3 |= num2 == 0;
                    if ((num10 > 0) && flag3)
                    {
                        int num11 = Math.Min(tokenIdx + num10, count);
                        if (emulateReplace)
                        {
                            for (int i = tokenIdx; i < num11; i++)
                            {
                                if (!this.Tokens[i].IsLiteral)
                                {
                                    ((PromptToken)this.Tokens[i]).ClearValue();
                                }
                            }
                        }
                        else
                        {
                            for (int j = tokenIdx; j < num11; j++)
                            {
                                if (this.Tokens[j].IsLiteral)
                                {
                                    num10--;
                                }
                            }
                            flag = !this.ShiftSymbols(tokenIdx, -num10);
                        }
                    }
                }
                flag |= !flag3;
            }
            finally
            {
                if (flag)
                {
                    cursorPosition = -1;
                    this.CancelEdit();
                }
                else
                {
                    this.EndEdit();
                }
            }
            return !flag;
        }

        private bool ShiftSymbols(int offset, int shift)
        {
            if (shift != 0)
            {
                if (shift > 0)
                {
                    int num = this.Tokens.Count - 1;
                    int num2 = num + 1;
                    for (int i = 0; i <= shift; i++)
                    {
                        num2 = this.FindNextPrompt(num2, false);
                        if (num2 < 0)
                        {
                            return false;
                        }
                        if (i == 0)
                        {
                            num = num2;
                        }
                        PromptToken token = (PromptToken)this.Tokens[num2];
                        if ((i != shift) && (!token.IsEmpty || (num2 < offset)))
                        {
                            return false;
                        }
                    }
                    int num4 = num2;
                    for (int j = num; num4 >= offset; j = this.FindNextPrompt(j, false))
                    {
                        PromptToken token2 = (PromptToken)this.Tokens[j];
                        PromptToken token3 = (PromptToken)this.Tokens[num4];
                        if (token3.IsEmpty)
                        {
                            token2.ClearValue();
                        }
                        else
                        {
                            string symbol = token3.Value;
                            if (this.CanApplySymbolToToken(symbol, token2) == SymbolMatchResult.MatchedPrompt)
                            {
                                token2.Value = symbol;
                            }
                            else
                            {
                                return false;
                            }
                        }
                        num4 = this.FindNextPrompt(num4, false);
                    }
                }
                else
                {
                    shift = -shift;
                    int num6 = offset;
                    int num7 = num6 - 1;
                    for (int k = 0; k < shift; k++)
                    {
                        num7 = this.FindNextPrompt(num7, true);
                        if (num7 < 0)
                        {
                            return false;
                        }
                        if (k == 0)
                        {
                            num6 = num7;
                        }
                        PromptToken token1 = (PromptToken)this.Tokens[num7];
                    }
                    int num9 = this.FindNextPrompt(num7, true);
                    for (int m = num6; m >= 0; m = this.FindNextPrompt(m, true))
                    {
                        PromptToken token4 = (PromptToken)this.Tokens[m];
                        if (num9 >= 0)
                        {
                            PromptToken token5 = (PromptToken)this.Tokens[num9];
                            if (token5.IsEmpty)
                            {
                                token4.ClearValue();
                            }
                            else
                            {
                                string str2 = token5.Value;
                                if (this.CanApplySymbolToToken(str2, token4) == SymbolMatchResult.MatchedPrompt)
                                {
                                    token4.Value = str2;
                                }
                                else
                                {
                                    return false;
                                }
                            }
                            num9 = this.FindNextPrompt(num9, true);
                        }
                        else
                        {
                            token4.ClearValue();
                        }
                    }
                }
            }
            return true;
        }

        public string TranslateContextDependentLiteral(string symbol)
        {
            Func<string, TokenizedMask, string> func;
            if (this._contextLiteralToValueDelegateMap.TryGetValue(symbol, out func))
            {
                return func(symbol, this);
            }
            return symbol;
        }

        private void UpdateCulture()
        {
            if (!this.IsEmpty)
            {
                CultureInfo culture = this.Culture;
                List<PromptToken> list = new List<PromptToken>(this.Tokens.Count);
                for (int i = 0; i < this.Tokens.Count; i++)
                {
                    PromptToken item = this.Tokens[i] as PromptToken;
                    if (item != null)
                    {
                        list.Add(item);
                    }
                }
                if (list.Count != this.Tokens.Count)
                {
                    this.UpdateMask(this._maskSymbols, false);
                    int num2 = 0;
                    int num3 = 0;
                    while ((num2 < this.Tokens.Count) && (num3 < list.Count))
                    {
                        PromptToken token2 = this.Tokens[num2] as PromptToken;
                        if (token2 != null)
                        {
                            PromptToken token3 = list[num3];
                            if (!token3.IsEmpty)
                            {
                                token2.Value = token3.Value;
                            }
                            num3++;
                        }
                        num2++;
                    }
                }
            }
        }

        public void UpdateMask(IList<string> maskSymbols, bool tryToKeepContent)
        {
            this._maskSymbols = maskSymbols;
            IList<string> addedSymbols = null;
            if (tryToKeepContent)
            {
                addedSymbols = this.GetTextSymbols(MaskFormat.ExcludePromptAndLiterals);
            }
            this.Tokens.Clear();
            bool? isUppercase = null;
            bool flag = false;
            for (int i = 0; i < maskSymbols.Count; i++)
            {
                string symbol = maskSymbols[i];
                bool flag2 = true;
                if (flag)
                {
                    goto Label_00DF;
                }
                if (this.IsPromptSymbol(symbol))
                {
                    PromptToken item = new PromptToken(this, symbol, isUppercase);
                    this.Tokens.Add(item);
                    flag2 = false;
                    goto Label_00DF;
                }
                if (!this.IsControlSymbol(symbol))
                {
                    goto Label_00B9;
                }
                string str3 = symbol;
                if (str3 == null)
                {
                    goto Label_00AC;
                }
                if (!(str3 == "<"))
                {
                    if (str3 == ">")
                    {
                        goto Label_00A2;
                    }
                    goto Label_00AC;
                }
                isUppercase = false;
                goto Label_00B4;
            Label_00A2:
                isUppercase = true;
                goto Label_00B4;
            Label_00AC:
                isUppercase = null;
            Label_00B4:
                flag2 = false;
                goto Label_00DF;
            Label_00B9:
                if (this.IsCustomPromptMarker(symbol) && (i == (maskSymbols.Count - 2)))
                {
                    this.PromptPresenter = maskSymbols[i + 1];
                    break;
                }
            Label_00DF:
                if (flag2)
                {
                    if (!flag && (symbol == @"\"))
                    {
                        flag = true;
                    }
                    else
                    {
                        string str = flag ? symbol : this.TranslateContextDependentLiteral(symbol);
                        IList<string> list2 = CompositeTextUtils.ConvertToSymbolList(str);
                        for (int j = 0; j < list2.Count; j++)
                        {
                            LiteralToken token2 = new LiteralToken(this, list2[j]);
                            this.Tokens.Add(token2);
                        }
                        flag = false;
                    }
                }
            }
            if (tryToKeepContent)
            {
                int num3;
                this.ProcessTextChange(0, null, addedSymbols, 0, false, out num3);
            }
        }

        // Properties
        public CultureInfo Culture
        {
            get
            {
                return (this._culture ?? CultureInfo.CurrentCulture);
            }
            set
            {
                if (this._culture != value)
                {
                    this._culture = value;
                    this.UpdateCulture();
                }
            }
        }

        public bool IsEmpty
        {
            get
            {
                return (this.Tokens.Count == 0);
            }
        }

        public string PromptPresenter
        {
            get
            {
                return this._promptPresenter;
            }
            set
            {
                this._promptPresenter = string.IsNullOrEmpty(value) ? "_" : CompositeTextUtils.ConvertToSymbolList(value)[0];
            }
        }

        public MaskTokenCollection Tokens
        {
            get
            {
                return this._tokens;
            }
        }

        // Nested Types
        private class ContextDependentLiteralToActualSymbolDelegateMap : Dictionary<string, Func<string, TokenizedMask, string>>
        {
        }

        private class PromptSymbolInfo
        {
            // Fields
            private readonly bool _isRequired;
            private readonly Func<string, bool> _matchDelegate;

            // Methods
            public PromptSymbolInfo(Func<string, bool> matchDelegate, bool isRequired)
            {
                this._matchDelegate = matchDelegate;
                this._isRequired = isRequired;
            }

            // Properties
            public bool IsRequired
            {
                get
                {
                    return this._isRequired;
                }
            }

            public Func<string, bool> MatchDelegate
            {
                get
                {
                    return this._matchDelegate;
                }
            }
        }

        private class PromptSymbolToMatchDelegateMap : Dictionary<string, TokenizedMask.PromptSymbolInfo>
        {
        }

        private enum SymbolMatchResult
        {
            MatchedPrompt,
            MatchedLiteral,
            InvalidPrompt,
            InvalidLiteral
        }
    }
}
